import hashlib
import logging
import os
import shutil
import socket
import ssl
import subprocess
import sys
import unittest

import m3u8
import util


__author__  = 'ZL <837806295@qq.com>'
__product__ = 'test'
__version__ = '3.0.0'
__date__    = '2023/04/08'


log = logging.getLogger(__product__)


class TestCase101(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_os_path_split(self):
        location, filename = os.path.split('/a.txt')
        self:assertEqual(location, '/')
        self.assertEqual(filename, 'a.txt')

    def test_range(self):
        self.assertFalse(set([0, 2, 4]) ^ set(range(0, 6, 2)))
        self.assertFalse(set([0, 2, 4, 6]) ^ set(range(0, 7, 2)))

    def test_hashlig_sha512(self):
        m = hashlib.sha512()
        m.update(b'')
        self.assertNotEqual(m.hexdigest(), '')

class TestCase102(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_is_supported_url(self):
        self.assertTrue(
            util.is_supported_url('http://abc.com:8080/abc/abc.m3u8'))
        self.assertTrue(
            util.is_supported_url('https://abc.com:8080/abc/abc.m3u8'))
        self.assertTrue(util.is_supported_url('http://abc.com/abc/abc.m3u8'))
        self.assertTrue(util.is_supported_url('https://abc.com/abc/abc.m3u8'))
        self.assertTrue(util.is_supported_url('http://abc.com/abc.m3u8'))
        self.assertTrue(util.is_supported_url('https://abc.com/abc.m3u8'))
        self.assertTrue(util.is_supported_url('http://abc.com/'))
        self.assertTrue(util.is_supported_url('https://abc.com/'))
        self.assertTrue(util.is_supported_url('http://abc.com'))
        self.assertTrue(util.is_supported_url('https://abc.com'))
        self.assertTrue(util.is_supported_url('http://abc'))
        self.assertTrue(util.is_supported_url('https://abc'))

        self.assertFalse(util.is_supported_url(''))
        self.assertFalse(util.is_supported_url('http://abc.com/abc//abc.m3u8'))
        self.assertFalse(util.is_supported_url('https://abc.com/abc//abc.m3u8'))
        self.assertFalse(util.is_supported_url('http://abc.com//abc'))
        self.assertFalse(util.is_supported_url('https://abc.com//abc'))
        self.assertFalse(util.is_supported_url('http://abc.com//'))
        self.assertFalse(util.is_supported_url('https://abc.com//'))
        self.assertFalse(util.is_supported_url('http://abc:'))
        self.assertFalse(util.is_supported_url('https://abc:'))
        self.assertFalse(util.is_supported_url('http://'))
        self.assertFalse(util.is_supported_url('https://'))
        self.assertFalse(util.is_supported_url('http:///'))
        self.assertFalse(util.is_supported_url('https:///'))
        self.assertFalse(util.is_supported_url('://'))
        self.assertFalse(util.is_supported_url(':///'))
        self.assertFalse(util.is_supported_url('//'))
        self.assertFalse(util.is_supported_url('///'))
        self.assertFalse(util.is_supported_url('ftp://abc.com/abc.m3u8'))
        self.assertFalse(util.is_supported_url('abc'))

    def test_get_url_root(self):
        self.assertEqual(util.get_url_root('http://abc.com/abc/abc.m3u8'),
                         'http://abc.com')
        self.assertEqual(util.get_url_root('https://abc.com/abc/abc.m3u8'),
                         'https://abc.com')
        self.assertEqual(util.get_url_root('http://abc.com/abc'),
                         'http://abc.com')
        self.assertEqual(util.get_url_root('https://abc.com/abc'),
                         'https://abc.com')
        self.assertEqual(util.get_url_root('http://abc.com/'),
                         'http://abc.com')
        self.assertEqual(util.get_url_root('https://abc.com/'),
                         'https://abc.com')
        self.assertEqual(util.get_url_root('http://abc.com'),
                         'http://abc.com')
        self.assertEqual(util.get_url_root('https://abc.com'),
                         'https://abc.com')
        self.assertEqual(util.get_url_root('http://abc/'),
                         'http://abc')
        self.assertEqual(util.get_url_root('https://abc/'),
                         'https://abc')
        self.assertEqual(util.get_url_root('http://abc'),
                         'http://abc')
        self.assertEqual(util.get_url_root('https://abc'),
                         'https://abc')

        self.assertFalse(util.get_url_root(''))
        self.assertFalse(util.get_url_root('abc'))

    def test_get_relative_url_head(self):
        self.assertEqual(
            util.get_relative_url_head('http://abc.com/abc/abc.m3u8'),
            'http://abc.com/abc')
        self.assertEqual(
            util.get_relative_url_head('https://abc.com/abc/abc.m3u8'),
            'https://abc.com/abc')
        self.assertEqual(util.get_relative_url_head('http://abc.com/abc'),
                         'http://abc.com')
        self.assertEqual(util.get_relative_url_head('https://abc.com/abc'),
                         'https://abc.com')
        self.assertEqual(util.get_relative_url_head('http://abc.com/'),
                         'http://abc.com')
        self.assertEqual(util.get_relative_url_head('https://abc.com/'),
                         'https://abc.com')
        self.assertEqual(util.get_relative_url_head('http://abc.com'),
                         'http://abc.com')
        self.assertEqual(util.get_relative_url_head('https://abc.com'),
                         'https://abc.com')
        self.assertEqual(util.get_relative_url_head('http://abc/'),
                         'http://abc')
        self.assertEqual(util.get_relative_url_head('https://abc/'),
                         'https://abc')
        self.assertEqual(util.get_relative_url_head('http://abc'),
                         'http://abc')
        self.assertEqual(util.get_relative_url_head('https://abc'),
                         'https://abc')

        self.assertFalse(util.get_relative_url_head(''))
        self.assertFalse(util.get_relative_url_head('abc'))

class TestCase103(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_is_sequence_line(self):
        self.assertEqual(m3u8.is_sequence_line('#EXT-X-MEDIA-SEQUENCE:0'), '0')
        self.assertEqual(
            m3u8.is_sequence_line('  #  EXT-X-MEDIA-SEQUENCE  :  0  '),
            '0')

        self.assertFalse(m3u8.is_sequence_line(''))
        self.assertFalse(m3u8.is_sequence_line('#EXT-X-MEDIA-SEQUENCE:'))
        self.assertFalse(m3u8.is_sequence_line('#EXT-X-MEDIA-SEQUENCE:abc'))
        self.assertFalse(m3u8.is_sequence_line('abc'))

    def test_is_key_line(self):
        self.assertTrue(m3u8.is_key_line('#EXT-X-KEY:abc'))
        self.assertTrue(m3u8.is_key_line('  #  EXT-X-KEY  :  abc  '))

        self.assertFalse(m3u8.is_key_line(''))
        self.assertFalse(m3u8.is_key_line('abc'))

    def test_is_ts_line(self):
        self.assertEqual(m3u8.is_ts_line('abc.ts'), 'abc.ts')
        self.assertEqual(m3u8.is_ts_line('  abc.ts  '), 'abc.ts')
        self.assertEqual(m3u8.is_ts_line('/abc.ts'), '/abc.ts')
        self.assertEqual(m3u8.is_ts_line('  /abc.ts  '), '/abc.ts')
        self.assertEqual(m3u8.is_ts_line('http://abc.com/abc.ts'),
                         'http://abc.com/abc.ts')
        self.assertEqual(m3u8.is_ts_line('  http://abc.com/abc.ts  '),
                         'http://abc.com/abc.ts')
        self.assertEqual(m3u8.is_ts_line('abc'), 'abc')
        self.assertEqual(m3u8.is_ts_line('  abc  '), 'abc')

        self.assertFalse(m3u8.is_ts_line(''))
        self.assertFalse(m3u8.is_ts_line('#abc'))
        self.assertFalse(m3u8.is_ts_line('  #  abc  '))

class TestCase104(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_get_encryption_method(self):
        self.assertEqual(
            m3u8.get_encryption_method(
                '#EXT-X-KEY:METHOD=AES-128,URI="https://abc.com/abc/abc.key",IV=0x000102030405060708090a0b0c0d0e0f'),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method(
                '#EXT-X-KEY:URI="https://abc.com/abc/abc.key",IV=0x000102030405060708090a0b0c0d0e0f,METHOD=AES-128'),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method(
                '#EXT-X-KEY:METHOD=AES-128,URI="https://abc.com/abc/abc.key"'),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method(
                '#EXT-X-KEY:URI="https://abc.com/abc/abc.key",METHOD=AES-128'),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method('#EXT-X-KEY:METHOD=NONE'),
            'NONE')
        self.assertEqual(
            m3u8.get_encryption_method(
                '  #  EXT-X-KEY  :  METHOD  =  AES-128  ,  URI  =  "https://abc.com/abc/abc.key"  ,  IV  =  0x000102030405060708090a0b0c0d0e0f  '),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method(
                '  #  EXT-X-KEY  :  URI  =  "https://abc.com/abc/abc.key"  ,  IV  =  0x000102030405060708090a0b0c0d0e0f  ,  METHOD  =  AES-128  '),
            'AES-128')
        self.assertEqual(
            m3u8.get_encryption_method('  #  EXT-X-KEY  :  METHOD  =  NONE  '),
            'NONE')

        self.assertFalse(m3u8.get_encryption_method(''))
        self.assertFalse(m3u8.get_encryption_method('#EXT-X-KEY:'))
        self.assertFalse(m3u8.get_encryption_method('#EXT-X-KEY:abc'))
        self.assertFalse(m3u8.get_encryption_method('abc'))

    def test_get_key_url(self):
        self.assertEqual(
            m3u8.get_key_url(
                '#EXT-X-KEY:METHOD=AES-128,URI="https://abc.com/abc/abc.key",IV=0x000102030405060708090a0b0c0d0e0f'),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '#EXT-X-KEY:METHOD=AES-128,IV=0x000102030405060708090a0b0c0d0e0f,URI="https://abc.com/abc/abc.key"'),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '#EXT-X-KEY:METHOD=AES-128,URI="https://abc.com/abc/abc.key"'),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '#EXT-X-KEY:URI="https://abc.com/abc/abc.key",METHOD=AES-128'),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '  #  EXT-X-KEY  :  METHOD  =  AES-128  ,  URI  =  "https://abc.com/abc/abc.key"  ,  IV  =  0x000102030405060708090a0b0c0d0e0f  '),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '  #  EXT-X-KEY  :  METHOD  =  AES-128  ,  IV  =  0x000102030405060708090a0b0c0d0e0f  ,  URI  =  "https://abc.com/abc/abc.key"  '),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '  #  EXT-X-KEY  :  METHOD  =  AES-128  ,  URI  =  "https://abc.com/abc/abc.key"  '),
            'https://abc.com/abc/abc.key')
        self.assertEqual(
            m3u8.get_key_url(
                '  #  EXT-X-KEY  :  URI  =  "https://abc.com/abc/abc.key"  ,  METHOD  =  AES-128  '),
            'https://abc.com/abc/abc.key')

        self.assertFalse(m3u8.get_key_url(''))
        self.assertFalse(m3u8.get_key_url('#EXT-X-KEY:METHOD=NONE'))
        self.assertFalse(m3u8.get_key_url('#EXT-X-KEY:'))
        self.assertFalse(m3u8.get_key_url('#EXT-X-KEY:abc'))
        self.assertFalse(m3u8.get_key_url('abc'))

    def test_get_encryption_iv(self):
        self.assertEqual(
            m3u8.get_encryption_iv(
                '#EXT-X-KEY:METHOD=AES-128,URI="https://abc.com/abc/abc.key",IV=0x000102030405060708090a0b0c0d0e0f'),
            '000102030405060708090a0b0c0d0e0f')
        self.assertEqual(
            m3u8.get_encryption_iv(
                '#EXT-X-KEY:IV=0x000102030405060708090a0b0c0d0e0f,METHOD=AES-128,URI="https://abc.com/abc/abc.key"'),
            '000102030405060708090a0b0c0d0e0f')
        self.assertEqual(
            m3u8.get_encryption_iv(
                '  #  EXT-X-KEY  :  METHOD  =  AES-128  ,  URI  =  "https://abc.com/abc/abc.key"  ,  IV  =  0x000102030405060708090a0b0c0d0e0f  '),
            '000102030405060708090a0b0c0d0e0f')
        self.assertEqual(
            m3u8.get_encryption_iv(
                '#  EXT-X-KEY  :  IV  =  0x000102030405060708090a0b0c0d0e0f  ,  METHOD  =  AES-128  ,  URI  =  "https://abc.com/abc/abc.key"  '),
            '000102030405060708090a0b0c0d0e0f')

        self.assertFalse(m3u8.get_encryption_iv(''))
        self.assertFalse(m3u8.get_encryption_iv('#EXT-X-KEY:METHOD=NONE'))
        self.assertFalse(m3u8.get_encryption_iv('#EXT-X-KEY:'))
        self.assertFalse(m3u8.get_encryption_iv('#EXT-X-KEY:abc'))
        self.assertFalse(m3u8.get_encryption_iv('abc'))

    def test_complete_url(self):
        self.assertEqual(
            util.complete_url('http://abc.com/abc/abc.m3u8',
                              'http://abc.com',
                              'http://abc.com/abc'),
            'http://abc.com/abc/abc.m3u8')
        self.assertEqual(
            util.complete_url('/abc.m3u8',
                              'http://abc.com',
                              'http://abc.com/abc'),
            'http://abc.com/abc.m3u8')
        self.assertEqual(
            util.complete_url('abc.m3u8',
                              'http://abc.com',
                              'http://abc.com/abc'),
            'http://abc.com/abc/abc.m3u8')

        self.assertFalse(util.complete_url('',
                                           'http://abc.com',
                                           'http://abc.com/abc'))

    def test_parse_m3u8_index(self):
        self.assertTrue(
            m3u8.parse_m3u8_index(
                'https://cdn6.hdzyk-cdn.com/20220918/15695_8f7a9ca5/1000k/hls/index.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case04_index.m3u8')))
        self.assertTrue(
            m3u8.parse_m3u8_index(
                'https://vip.lz-cdn3.com/20230114/17154_db8125dc/1800k/hls/mixed.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case05_index.m3u8')))
        self.assertTrue(
            m3u8.parse_m3u8_index(
                'https://hey07.cjkypo.com/20220618/VX2JbPHa/hls/index.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case06_index.m3u8')))

        self.assertFalse(
            m3u8.parse_m3u8_index(
                'https://abc.com/abc/abc.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case00_index.m3u8')))
        self.assertFalse(
            m3u8.parse_m3u8_index(
                'https://abc.com/abc/abc.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case01_index.m3u8')))
        self.assertFalse(
            m3u8.parse_m3u8_index(
                'https://abc.com/abc/abc.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case02_index.m3u8')))
        self.assertFalse(
            m3u8.parse_m3u8_index(
                'https://abc.com/abc/abc.m3u8',
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case03_index.m3u8')))

class TestCase105(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

        cls.task_info = m3u8.get_task_config(
            util.get_test_subdir(__file__, ['data', 'config']),
            'case07')
        cls.task_info.append(
            {
                'key_data': [b'' for i in range(len(cls.task_info[0]['keys']))],
            }
        )

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

        del cls.task_info

    def setUp(self):
        self.work_dir = self.__class__.work_dir

        self.task_info = self.__class__.task_info

    def tearDown(self):
        del self.work_dir

        del self.task_info

    def test_get_task_config(self):
        self.assertTrue(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case06'))

        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case00'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case01'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case02'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case03'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case04'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case05'))

    def test_has_task_config(self):
        self.assertTrue(
            m3u8.has_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case06'))

        self.assertFalse(
            m3u8.has_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case00'))
        self.assertFalse(
            m3u8.has_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case01'))
        self.assertFalse(
            m3u8.has_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case02'))

    def test_get_task_config(self):
        self.assertTrue(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case07'))

        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case00'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case01'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case02'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case03'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case04'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case05'))
        self.assertFalse(
            m3u8.get_task_config(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case06'))

    def test_update_task_config(self):
        self.assertTrue(
            m3u8.update_task_config(self.task_info,
                                    False,
                                    self.work_dir,
                                    'case1'))
        self.assertTrue(
            m3u8.update_task_config(self.task_info,
                                    True,
                                    self.work_dir,
                                    'case2'))

        self.assertFalse(
            m3u8.update_task_config([{}, {}, {}],
                                    True,
                                    self.work_dir,
                                    'case3'))

    def test_is_finished_task(self):
        self.assertTrue(
            m3u8.is_finished_task(
                'https://vip.lz-cdn3.com/20230114/17154_db8125dc/1800k/hls/mixed.m3u8',
                util.get_test_subdir(__file__, ['data', 'config']),
                'case09'))

        self.assertFalse(
            m3u8.is_finished_task(
                'https://vip.lz-cdn3.com/20230114/17154_db8125dc/1800k/hls/mixed.m3u8',
                util.get_test_subdir(__file__, ['data', 'config']),
                'case06'))
        self.assertFalse(
            m3u8.is_finished_task(
                'https://vip.lz-cdn3.com/20230114/17154_db8125dc/1800k/hls/mixed.m3u8',
                util.get_test_subdir(__file__, ['data', 'config']),
                'case07'))
        self.assertFalse(
            m3u8.is_finished_task(
                'https://vip.lz-cdn3.com/20230114/17154_db8125dc/1800k/hls/mixed.m3u8',
                util.get_test_subdir(__file__, ['data', 'config']),
                'case08'))

    def test_check_breakpoint(self):
        self.assertTrue(
            m3u8.check_breakpoint(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case08',
                self.task_info))
        self.assertTrue(
            m3u8.check_breakpoint(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case09',
                self.task_info))

        self.assertFalse(
            m3u8.check_breakpoint(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case06',
                self.task_info))
        self.assertFalse(
            m3u8.check_breakpoint(
                util.get_test_subdir(__file__, ['data', 'config']),
                'case07',
                self.task_info))

class TestCase106(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_check_status_code_206(self):
        case1 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes 0-375059/375060\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375060\r\n'
        )

        case2 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes */375060\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375060\r\n'
        )

        case3 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes 0-375059/*\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375060\r\n'
        )

        case4 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes 0-375049/375060\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375050\r\n'
        )

        case5 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes 0-375049/*\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375050\r\n'
        )

        self.assertTrue(util.check_status_code_206(case1))
        self.assertTrue(util.check_status_code_206(case2))

        self.assertFalse(util.check_status_code_206(case3))
        self.assertFalse(util.check_status_code_206(case4))
        self.assertFalse(util.check_status_code_206(case5))

    def test_check_status_code(self):
        case1 = (
            b'HTTP/1.1 200 OK\r\n'
            b'Date: Thu, 06 Apr 2023 16:36:23 GMT\r\n'
            b'Content-Type: application/vnd.apple.mpegURL\r\n'
            b'Transfer-Encoding: chunked\r\n'
            b'Connection: keep-alive\r\n'
            b'X-Powered-By: Express\r\n'
            b'Access-Control-Allow-Origin: *\r\n'
            b'Access-Control-Allow-Headers: DNT, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type\r\n'
            b'Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n'
            b'Server: cdn\r\n'
            b'Access-Control-Expose-Headers: Content-Length, Content-Range\r\n'
            b'Access-Control-Allow-Credentials: true\r\n'
            b'X-Cache-Status: HIT\r\n'
            b'\r\n'
        )

        case2 = (
            b'HTTP/2 206 \r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes */375060\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375060\r\n'
            b'\r\n'
        )

        case3 = (
            b'HTTP/2 206\r\n'
            b'accept-ranges: bytes\r\n'
            b'access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type\r\n'
            b'access-control-allow-methods: GET, POST, OPTIONS\r\n'
            b'access-control-allow-origin: *\r\n'
            b'cache-control: max-age=2592000\r\n'
            b'content-range: bytes */375060\r\n'
            b'content-type: video/mp2t\r\n'
            b'date: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'expires: Sat, 08 Apr 2023 15:19:02 GMT\r\n'
            b'last-modified: Thu, 09 Mar 2023 15:19:02 GMT\r\n'
            b'server: nginx\r\n'
            b'x-cache: HIT, server, disk\r\n'
            b'content-length: 375060\r\n'
            b'\r\n'
        )

        self.assertTrue(util.check_status_code(case1))
        self.assertTrue(util.check_status_code(case2))
        self.assertTrue(util.check_status_code(case3))

    def test_check_response(self):
        self.assertTrue(
            util.check_response(
                os.path.join(
                    util.get_test_subdir(__file__, ['data', 'curl_response']),
                    'case01_00000000000000000001.ts.curlresponse'),
                os.path.join(self.work_dir,
                             'case01_00000000000000000001.ts')))
        self.assertTrue(
            util.check_response(
                os.path.join(
                    util.get_test_subdir(__file__, ['data', 'curl_response']),
                    'case02_00000000000000000002.ts.curlresponse'),
                os.path.join(self.work_dir,
                             'case02_00000000000000000002.ts')))

    def test_decrypt_ts(self):
        shutil.copyfile(
            os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                         'case06_index.m3u8_bj2gFcDA.ts'),
            os.path.join(self.work_dir, 'case06_index.m3u8_bj2gFcDA.ts'))
        self.assertTrue(
            m3u8.decrypt_ts(
                os.path.join(self.work_dir, 'case06_index.m3u8_bj2gFcDA.ts'),
                'AES-128',
                b'33e6e95aebaae9df',
                '',
                2))
        self.assertTrue(
            util.compare_files(
                os.path.join(util.get_test_subdir(__file__, ['data', 'm3u8']),
                             'case06_index.m3u8_bj2gFcDA-plain.ts'),
                os.path.join(self.work_dir,
                             'case06_index.m3u8_bj2gFcDA.ts')))

class TestCase107(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.work_dir = util.get_test_subdir(__file__, cls.__name__)

    @classmethod
    def tearDownClass(cls):
        del cls.work_dir
        util.remove_test_subdir(__file__, cls.__name__)

    def setUp(self):
        self.work_dir = self.__class__.work_dir

    def tearDown(self):
        del self.work_dir

    def test_is_task_line(self):
        self.assertTrue(m3u8.is_task_line('abc'))
        self.assertTrue(m3u8.is_task_line('  abc  '))
        self.assertTrue(m3u8.is_task_line('abc#'))
        self.assertTrue(m3u8.is_task_line('"  abc  "'))

        self.assertFalse(m3u8.is_task_line(''))
        self.assertFalse(m3u8.is_task_line(' '))
        self.assertFalse(m3u8.is_task_line('# abc'))
        self.assertFalse(m3u8.is_task_line('  # abc'))

    def test_get_line_content(self):
        self.assertEqual(m3u8.get_line_content('abc'), 'abc')
        self.assertEqual(m3u8.get_line_content('  ab  c  '), 'ab  c')
        self.assertEqual(m3u8.get_line_content('"  abc  "'), '  abc  ')
        self.assertEqual(m3u8.get_line_content('"'), '"')
        self.assertEqual(m3u8.get_line_content('"abc'), '"abc')
        self.assertEqual(m3u8.get_line_content('""'), '')
        self.assertEqual(m3u8.get_line_content('"""'), '"')
        self.assertEqual(m3u8.get_line_content(''), '')

    def test_get_tasks(self):
        self.assertTrue(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case06'])))

        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case00'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case01'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case02'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case03'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case04'])))
        self.assertFalse(
            m3u8.get_tasks(
                util.get_test_subdir(__file__,
                                     ['data', 'batch_list', 'case05'])))

class UnorderedTestLoader(unittest.TestLoader):
    sortTestMethodsUsing = lambda self, x, y: -1


if __name__ == '__main__':
    util.set_log(__file__, '%s_v%s' % (__product__, __version__), 'debug')

    log.info('program version: %s %s', __product__, __version__)
    log.debug('command line: %s', sys.argv)

    # 避免目标网站因HTTPS证书问题而导致请求失败
    ssl._create_default_https_context = ssl._create_unverified_context

    # 设置下载超时，这个是全局设置
    socket.setdefaulttimeout(30)

    unittest.main(testLoader=UnorderedTestLoader())
